﻿namespace BorgExt
{
    public class Slot : MetaElement
    {
        internal bool reflectionHandlersAdded;
        internal bool isOpen;

        internal BehaviorCollection behaviors;
        internal MetaElementCollection<Plug> pluggedPlugs;
        internal MetaElementCollection<Plug> selectedPlugs;

        internal CompositionEventHandler<SlotEventArgs>[] OpenedEventHandlers { get; set; }
        internal CompositionEventHandler<CompositionEventArgs>[] PluggedEventHandlers { get; set; }
        internal CompositionEventHandler<CompositionEventArgs>[] SelectedEventHandlers { get; set; }

        internal CompositionEventHandler<SlotEventArgs>[] ClosedEventHandlers { get; set; }
        internal CompositionEventHandler<CompositionEventArgs>[] UnpluggedEventHandlers { get; set; }
        internal CompositionEventHandler<CompositionEventArgs>[] DeselectedEventHandlers { get; set; }

        public BehaviorCollection Behaviors
        {
            get
            {
                lock (LockObject)
                    return behaviors
                           ?? (behaviors =
                               new BehaviorCollection(OnBehaviorAdded, OnBehaviorRemoved));
            }
        }

        public Extension Extension { get; private set; }
        public SlotType SlotType { get; private set; }

        public bool AutoOpen { get; set; }
        public bool AutoPlug { get; set; }
        public bool AutoSelect { get; set; }

        public bool Shared { get; set; }

        public MetaElementCollection<Plug> PluggedPlugs
        {
            get { return InstanceStore.GetPluggedPlugs(this); }
        }

        public MetaElementCollection<Plug> SelectedPlugs
        {
            get { return InstanceStore.GetSelectedPlugs(this); }
        }

        public event CompositionEventHandler<SlotEventArgs> Opened
        {
            add
            {
                lock (LockObject)
                    OpenedEventHandlers = ArrayEx.Add(OpenedEventHandlers, value);
            }
            remove
            {
                lock (LockObject)
                    OpenedEventHandlers = ArrayEx.Remove(OpenedEventHandlers, value);
            }
        }

        public event CompositionEventHandler<CompositionEventArgs> Plugged
        {
            add
            {
                lock (LockObject)
                    PluggedEventHandlers = ArrayEx.Add(PluggedEventHandlers, value);
            }
            remove
            {
                lock (LockObject)
                    PluggedEventHandlers = ArrayEx.Remove(PluggedEventHandlers, value);
            }
        }

        public event CompositionEventHandler<CompositionEventArgs> Selected
        {
            add
            {
                lock (LockObject)
                    SelectedEventHandlers = ArrayEx.Add(SelectedEventHandlers, value);
            }
            remove
            {
                lock (LockObject)
                    SelectedEventHandlers = ArrayEx.Remove(SelectedEventHandlers, value);
            }
        }

        public event CompositionEventHandler<SlotEventArgs> Closed
        {
            add
            {
                lock (LockObject)
                    ClosedEventHandlers = ArrayEx.Add(ClosedEventHandlers, value);
            }
            remove
            {
                lock (LockObject)
                    ClosedEventHandlers = ArrayEx.Remove(ClosedEventHandlers, value);
            }
        }

        public event CompositionEventHandler<CompositionEventArgs> Unplugged
        {
            add
            {
                lock (LockObject)
                    UnpluggedEventHandlers = ArrayEx.Add(UnpluggedEventHandlers, value);
            }
            remove
            {
                lock (LockObject)
                    UnpluggedEventHandlers = ArrayEx.Remove(UnpluggedEventHandlers, value);
            }
        }

        public event CompositionEventHandler<CompositionEventArgs> Deselected
        {
            add
            {
                lock (LockObject)
                    DeselectedEventHandlers = ArrayEx.Add(DeselectedEventHandlers, value);
            }
            remove
            {
                lock (LockObject)
                    DeselectedEventHandlers = ArrayEx.Remove(DeselectedEventHandlers, value);
            }
        }

        internal Slot(int id, SlotType slotType, Extension extension)
            : base(id, slotType.Name)
        {
            pluggedPlugs = new MetaElementCollection<Plug>();
            selectedPlugs = new MetaElementCollection<Plug>();
            
            SlotType = slotType;
            AutoOpen = slotType.AutoOpen;
            AutoPlug = slotType.AutoPlug;
            AutoSelect = slotType.AutoSelect;
            Shared = slotType.Shared;
            Extension = extension;
        }

        public bool IsOpen()
        {
            return InstanceStore.IsOpen(this);
        }

        public bool IsPlugged(Plug plug)
        {
            return InstanceStore.IsPlugged(this, plug);
        }

        public bool IsSelected(Plug plug)
        {
            return InstanceStore.IsSelected(this, plug);
        }

        public void Open()
        {
            Composer.Open(this);
        }

        public void Plug(Plug plug)
        {
            Composer.Plug(this, plug);
        }

        public void Select(Plug plug)
        {
            Composer.Select(this, plug);
        }

        public void Close()
        {
            Composer.Close(this);
        }

        public void Unplug(Plug plug)
        {
            Composer.Unplug(this, plug);
        }

        public void Deselect(Plug plug)
        {
            Composer.Deselect(this, plug);
        }

        internal void OnBehaviorAdded(IBehavior behavior)
        {
            behavior.Bind(this);
        }

        internal void OnBehaviorRemoved(IBehavior behavior)
        {
            behavior.Unbind(this);
        }

        internal override AssemblyElement GetAssemblyElement()
        {
            return Extension.ExtensionType.Plugin;
        }
    }
}

